package net.esj.basic.plugins.spring;

import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringFactory implements ApplicationContextAware{

	protected static final Logger logger = Logger
			.getLogger(SpringFactory.class);

	protected ApplicationContext appContext;
	protected AutowireCapableBeanFactory autoWiringFactory;
	protected int autowireStrategy = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;

	public void setApplicationContextPath(String ctx) {
		if (ctx != null) {
			setApplicationContext(new ClassPathXmlApplicationContext(ctx));
		}
	}

	public void setApplicationContext(ApplicationContext appContext)
			throws BeansException {
		this.appContext = appContext;
		autoWiringFactory = findAutoWiringBeanFactory(this.appContext);
	}

	public void setAutowireStrategy(int autowireStrategy) {
		switch (autowireStrategy) {
		case AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT:
			logger.info("Setting autowire strategy to autodetect");
			this.autowireStrategy = autowireStrategy;
			break;
		case AutowireCapableBeanFactory.AUTOWIRE_BY_NAME:
			logger.info("Setting autowire strategy to name");
			this.autowireStrategy = autowireStrategy;
			break;
		case AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE:
			logger.info("Setting autowire strategy to type");
			this.autowireStrategy = autowireStrategy;
			break;
		case AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR:
			logger.info("Setting autowire strategy to constructor");
			this.autowireStrategy = autowireStrategy;
			break;
		case AutowireCapableBeanFactory.AUTOWIRE_NO:
			logger.info("Setting autowire strategy to none");
			this.autowireStrategy = autowireStrategy;
			break;
		default:
			throw new IllegalStateException("Invalid autowire type set");
		}
	}

	public int getAutowireStrategy() {
		return autowireStrategy;
	}

	protected AutowireCapableBeanFactory findAutoWiringBeanFactory(
			ApplicationContext context) {
		if (context instanceof AutowireCapableBeanFactory) {
			// Check the context
			return (AutowireCapableBeanFactory) context;
		} else if (context instanceof ConfigurableApplicationContext) {
			// Try and grab the beanFactory
			return ((ConfigurableApplicationContext) context).getBeanFactory();
		} else if (context.getParent() != null) {
			// And if all else fails, try again with the parent context
			return findAutoWiringBeanFactory(context.getParent());
		}
		return null;
	}
	
	public Object autoWireBean(Object bean) {
        return autoWireBean(bean, autoWiringFactory);
    }

	public Object autoWireBean(Object bean,
			AutowireCapableBeanFactory autoWiringFactory) {
		if (autoWiringFactory != null) {
			autoWiringFactory.autowireBeanProperties(bean, autowireStrategy,
					false);
		}
		injectApplicationContext(bean);

		return bean;
	}

	private void injectApplicationContext(Object bean) {
		if (bean instanceof ApplicationContextAware) {
			((ApplicationContextAware) bean).setApplicationContext(appContext);
		}
	}
}
